#include "ct_config.h"
#include "ct_i2c.h"
#include "ct_sys.h"
//***********************************************************************************************************
//  File Function: ML51 I2C master mode demo code, the Slave address = 0xA4
//
//   ____________            _____________
//  |            |   SDA    |             |
//  |            |<-------->|             |
//  |            |          |             |
//  |ML51(M)     |          | ML51(S)     |
//  |(I2C_Master)|          | (I2C_Slave) |
//  |            |   SCL    |             |
//  |            |--------->|             |
//  |____________|          |_____________|
//
//  The protocol of I2C is master: start -> write 10 byte(ACK) ->stop -> start ->read 10
//  byte(ACK) -> stop
//***********************************************************************************************************

//========================================================================================================
void ct_i2c_Init(void) {
    ;
    P13_OPENDRAIN_MODE;    // Modify SCL pin to Open drain mode. don't forget the pull high resister in circuit
    P14_OPENDRAIN_MODE;    // Modify SDA pin to Open drain mode. don't forget the pull high resister in circuit
    /* The function enable the specify I2C controller and set proper clock divider
     * in I2C CLOCK DIVIDED REGISTER (I2CLK) according to the target I2C Bus clock.
     * I2C bus clock = PCLK / (4*(u32I2CCLK+1).*/
    I2C_Master_Open(24000000, 100000);
}
//========================================================================================================
void ct_i2c_Error(unsigned char err_code) {
    while (err_code)
        ;
}
//========================================================================================================

//--------------------------------------------------------------------------------------------
//----  Page Read
//----------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------
int ct_i2c_Read(unsigned char reg_addr, i2c_trans_t *i2c_readx) {
    SFRS = 0;
    /* Read Step1 */
    set_I2CON_STA;
    clr_I2CON_SI;
    while (!SI)
        ;                  // Check SI set or not
    if (I2STAT != 0x08)    // Check status value after every step
        I2C_Error();

    /* Step13 */
    clr_I2CON_STA;    // STA needs to be cleared after START codition is generated
    I2C1DAT = ((reg_addr << 1) | CT_I2C_RD);
    clr_I2CON_SI;
    while (!SI)
        ;    // Check SI set or not
    if (I2STAT != 0x40)
        I2C_Error();

    /* Step14 */
    set_I2C1CON_AA;
    clr_I2C1CON_SI;
    while (!(I2C1CON & SET_BIT3))
        ;    // Check SI set or not

    if (I2C1STAT != 0x50) ct_i2c_Error(7);

    i2c_readx->u8_data = I2C1DAT;

    /* Step15 */
    clr_I2C1CON_AA;
    clr_I2C1CON_SI;
    while (!(I2C1CON & SET_BIT3))
        ;    // Check SI set or not
    if (I2C1STAT != 0x58) ct_i2c_Error(9);

    /* Step16 */
    set_I2C1CON_STO;
    clr_I2C1CON_SI;
    while (I2C1CON & SET_BIT4)
        ; /* Check STOP signal */

    i2c_readx->error = 0;

    return i2c_readx->error;
}

//--------------------------------------------------------------------------------------------
//----  Page
// Write----------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------
int ct_i2c_Write(unsigned char reg_addr, unsigned char u8data, i2c_trans_t *i2c_write) {
    i2c_trans_t i2c_read_struct;
    SFRS = 0;
    /* Write Step1 */
    set_I2C1CON_STA; /* Send Start bit to I2C EEPROM */
    clr_I2C1CON_SI;
    while (!(I2C1CON & SET_BIT3))
        ;                 /*Check SI set or not  */
    if (I2C1STAT != 0x08) /*Check status value after every step   */
        ct_i2c_Error(1);

    /* Write Step2 */
    clr_I2C1CON_STA; /*STA=0*/
    I2C1DAT = ((reg_addr << 1) | CT_I2C_WR);
    clr_I2C1CON_SI;
    while (!(I2C1CON & SET_BIT3))
        ; /*Check SI set or not */
    if (I2C1STAT != 0x18) ct_i2c_Error(2);

    /* Write Step3 */
    I2C1DAT = u8data;
    clr_I2C1CON_SI;
    while (!(I2C1CON & SET_BIT3))
        ; /*Check SI set or not*/
    if (I2C1STAT != 0x28) ct_i2c_Error(3);

    /* Write Step4 */
    set_I2C1CON_STO;
    clr_I2C1CON_SI;
    while (I2C1CON & SET_BIT4)
        ; /* Check STOP signal */

    ct_i2c_Read(reg_addr, &i2c_read_struct);

    i2c_write->u8_data = i2c_read_struct.u8_data;

    if (i2c_read_struct.u8_data != u8data) { ct_i2c_Error(4); }

    i2c_write->error = 0;

    return i2c_write->error;
}
//================================================================================================

/**
 * @brief      Initialize I2C function
 * @param[in]  i2c:  I2C init structure
 * @return     None
 * @details    The function enable the specify I2C controller and set proper clock divider
 *             in I2C CLOCK DIVIDED REGISTER (I2CLK) according to the target I2C Bus clock.
 *             I2C bus clock = PCLK / (4*(u32I2CCLK+1).
 * @exmaple :  ct_i2c_MasterOpen(24000000,100000);
 */
void ct_i2c_MasterOpen(ct_i2c_init_t *i2c) {
    I2C_Master_Open(CT_SYSCLK, i2c->clock);
}

/**
 * @brief      Initialize I2C function
 * @param[in]  i2c:  I2C init structure
 * @return     None
 * @details    The target I2C bus clock in Hz. Use HIRC the I2C clock is from 23473 ~ 2000000
 * @exmaple :  ct_i2c_SlaveOpen(Slave Addr);
 */
void ct_i2c_SlaveOpen(ct_i2c_init_t *i2c) {
    I2C_Slave_Open(i2c->SlaveAddr);
}

/**
 * @brief      Disable I2C function
 * @param[in]  u8I2CSel:  Specify I2C port
 *                  - \ref I2C0
 *                  - \ref I2C1
 * @exmaple :  I2C0_Close(I2C0);
 */
#ifdef I2C_CHANNEL_
void ct_i2c_Close(unsigned char u8I2CSel) {
    I2C0_Close();
}
#endif
void ct_i2c_Close(unsigned char u8I2CSel) {
    CT_UNUSED(u8I2CSel);
    I2C_Close();
}

/**
 * @brief      Enable specify I2C controller interrupt, also need enable globle interrupt in main loop.
 * @param[in]  u8I2CSel:  Specify I2C port
 *                  - \ref I2C0
 *                  - \ref I2C1
 * @param[in]  u8I2CStatus:  Specify I2C interrupt status
 *                  - \ref Enable
 *                  - \ref Disable
 * @exmaple :  I2C_Interrupt(I2C0, Enable);
 */
void ct_i2c_Interrupt(unsigned char u8I2CSel, unsigned char u8I2CStatus) {
    I2C_Interrupt(u8I2CSel, u8I2CStatus);
}

/**
 * @brief      Get I2C bus status value
 * @param[in]  u8I2CSel:  Specify I2C port
 * @param[in]  u8I2CSel:  Specify I2C port
 *                  - \ref I2C0
 *                  - \ref I2C1
 * @return     I2C status data
 * @exmaple :  I2C_GetStatus(I2C0);
 */
unsigned char ct_i2c_GetStatus(unsigned char u8I2CSel) {
    unsigned char u8i2cstat;
    SFRS = 0;
    switch (u8I2CSel) {
        case I2C0:
            u8i2cstat = I2C0STAT;
            break;
        case I2C1:
            u8i2cstat = I2C1STAT;
            break;
        default:
            break;
    }
    return (u8i2cstat);
}

/**
 * @brief      Configure the mask bits of 7-bit Slave Address
 * @param[in]  u8I2CSel:  Specify I2C port
 *                  - \ref I2C0
 *                  - \ref I2C1
 * @param[in]  u8SlaveNo        Set the number of I2C address mask register (0~3)
 *                  - \ref 0
 *                  - \ref 1
 *                  - \ref 2
 *                  - \ref 3
 * @param[in]  u8SlaveAddrMask  A byte for slave address mask
 * @return     None
 * @details    This function is used to set 7-bit slave addresses.
 * @example    I2C_SetSlaveAddrMask(I2C0,0,0x33);
 */
void ct_i2c_SetSlaveaddrMask(unsigned char u8I2CSel, unsigned char u8SlaveNo, unsigned char u8SlaveAddrMask) {
    I2C_SetSlaveAddrMask(u8I2CSel, u8SlaveNo, u8SlaveAddrMask);
}

/**
 * @brief      Enable Time-out Function with support long time-out
 * @param[in]  u8I2CSel:  Specify I2C port
 *                  - \ref I2C0
 *                  - \ref I2C1
 * @param[in]  u8I2CTRStatus:  Specify I2C Timer status
 *                  - \ref Enable
 *                  - \ref Disable
 * @return     None
 * @Note       This function enable time-out function and configure DIV4 to support longer time-out.
 * @example    I2C_Timeout(I2C0,Enable);
 */

void ct_i2c_Timeout(unsigned char u8I2CSel, unsigned char u8I2CTRStatus) {
    I2C_Timeout(u8I2CSel, u8I2CTRStatus);
}

void ct_i2c_ClearTimeoutFlag(unsigned char u8I2CSel) {
    I2C_ClearTimeoutFlag(u8I2CSel);
}

/**
 * @brief      Special design for 8051 I2C SI check
 * @param[in]  none
 * @return     None
 * @details    This function setting the slave address mask bit.
 */
void ct_i2c0_SI_Check(void) {
    I2C0_SI_Check();
}

/**
 * @brief      Special design for 8051 I2C SI check
 * @param[in]  none
 * @return     None
 * @details    This function setting the slave address mask bit.
 */
void ct_i2c1_SI_Check(void) {
    I2C1_SI_Check();
}

#if 1

// 定义I2C状态码
#define I2C_START                 0x00
#define I2C_SLAW_ACK              0x60
#define I2C_SLAW_NACK             0x68
#define I2C_DATA_RECEIVED_ACK     0x80
#define I2C_DATA_RECEIVED_NACK    0x88
#define I2C_STOP_RESTART          0xA0
#define I2C_SLAR_ACK              0xA8
#define I2C_DATA_TRANSMITTED_ACK  0xB8
#define I2C_DATA_TRANSMITTED_NACK 0xC0
#define I2C_LAST_DATA_ACK         0xC8

// 全局变量
unsigned char data_received[34];
unsigned char data_num = 0;
unsigned int  u16ReadAddress;
bit           I2CWOVERFLAG = 0;

// 读取APROM字节
unsigned char ReadAPROM_BYTE(unsigned int code *u16_addr) {
    return *u16_addr >> 8;
}

// I2C从机处理函数
void I2c_slave(void) {
    SFRS                 = 0;
    unsigned char status = I2C0STAT;
    unsigned char data;

    switch (status) {
        case I2C_START:
            set_I2C0CON_STO;
            while (STO0)
                ;
            break;

        case I2C_SLAW_ACK:
            set_I2C0CON_AA;
            break;

        case I2C_SLAW_NACK:
            clr_I2C0CON_AA;
            break;

        case I2C_DATA_RECEIVED_ACK:
            data                    = I2C0DAT;
            data_received[data_num] = data;
            if (data_num == 0) {
                u16ReadAddress = data << 8;    // High byte of address
            } else if (data_num == 1) {
                u16ReadAddress |= data;    // Low byte of address
            }
            data_num++;
            if (data_num >= 32) {
                clr_I2C0CON_AA;
                data_num = 0;    // Reset data index after a full packet received
            } else {
                set_I2C0CON_AA;
            }
            break;

        case I2C_DATA_RECEIVED_NACK:
            data_num = 0;
            clr_I2C0CON_AA;
            break;

        case I2C_STOP_RESTART:
            if (data_num > 20) {
                I2CWOVERFLAG = 1;
            } else {
                I2CWOVERFLAG = 0;
            }
            data_num = 0;
            set_I2C0CON_AA;
            break;

        case I2C_SLAR_ACK:
        case I2C_DATA_TRANSMITTED_ACK:
        case I2C_DATA_TRANSMITTED_NACK:
            I2C0DAT = ReadAPROM_BYTE((unsigned int code *)(u16ReadAddress + data_num));
            data_num++;
            set_I2C0CON_AA;
            if (data_num >= 32) {
                data_num = 0;    // Reset data index after a full packet transmitted
            }
            break;

        case I2C_LAST_DATA_ACK:
            set_I2C0CON_AA;
            break;
    }

    I2C0_SI_Check();
}

#endif